Now that we have explored and transformed the data, it’s time to visualize the story hidden within it.
📈 Trends and patterns 🔗 Relationships between variables ⚠️ Outliers and unusual behaviour 💡 Business insights
In this phase, we’ll move from basic distributions to advanced visualizations and learn not just how to create charts, but how to interpret what they are telling us.
A good visualization doesn’t just show data—it helps us understand it.
import matplotlib.pyplot as plt
import seaborn as sns
sns.set(style="whitegrid")
plt.rcParams.update({'font.size': 10}) # change 10 → whatever you prefer
import pandas as pd
df = pd.read_csv('Online Retail Phase 2 Output.csv',index_col = 0)
df.head()
| CustomerID | InvoiceNo | StockCode | Description | Quantity | InvoiceDate | UnitPrice | Country | Revenue | Year | Month | Day | Hour | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 17850 | 536365 | 85123A | WHITE HANGING HEART T-LIGHT HOLDER | 6 | 2010-12-01 08:26:00 | 2.55 | United Kingdom | 15.30 | 2010 | 12 | 1 | 8 |
| 1 | 17850 | 536365 | 71053 | WHITE METAL LANTERN | 6 | 2010-12-01 08:26:00 | 3.39 | United Kingdom | 20.34 | 2010 | 12 | 1 | 8 |
| 2 | 17850 | 536365 | 84406B | CREAM CUPID HEARTS COAT HANGER | 8 | 2010-12-01 08:26:00 | 2.75 | United Kingdom | 22.00 | 2010 | 12 | 1 | 8 |
| 3 | 17850 | 536365 | 84029G | KNITTED UNION FLAG HOT WATER BOTTLE | 6 | 2010-12-01 08:26:00 | 3.39 | United Kingdom | 20.34 | 2010 | 12 | 1 | 8 |
| 4 | 17850 | 536365 | 84029E | RED WOOLLY HOTTIE WHITE HEART. | 6 | 2010-12-01 08:26:00 | 3.39 | United Kingdom | 20.34 | 2010 | 12 | 1 | 8 |
plt.figure(figsize=(5,5))
sns.boxplot(x=df['Quantity'])
plt.title("Box Plot of Quantity")
plt.show()
I plotted the data… and something looked off.”
Extreme outliers were completely distorting the visualization.
That’s when it clicked— visualization isn’t the end of cleaning, it’s part of it.
So instead of trusting the first plot, let’s remove outliers and plot again to see the difference.
# Function to remove outliers using IQR
def remove_outliers(df, col):
Q1 = df[col].quantile(0.25)
Q3 = df[col].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
return df[(df[col] >= lower_bound) & (df[col] <= upper_bound)]
# Apply on Quantity and UnitPrice
df_clean = remove_outliers(df, 'Quantity')
df_clean = remove_outliers(df_clean, 'UnitPrice')
import matplotlib.pyplot as plt
import seaborn as sns
# Style for better visuals
sns.set_style("whitegrid")
sns.set_context("talk")
plt.figure(figsize=(8,4))
# Quantity - Before vs After
plt.subplot(1, 2, 1)
sns.boxplot(y=df['Quantity'], color='lightcoral')
plt.title("Before Cleaning")
plt.subplot(1, 2, 2)
sns.boxplot(y=df_clean['Quantity'], color='seagreen')
plt.title("After Cleaning")
plt.suptitle("Impact of Outlier Removal on Quantity", fontsize=14)
plt.tight_layout()
plt.show()
# Unit Price - Before vs After
plt.figure(figsize=(8,4))
plt.subplot(1, 2, 1)
sns.boxplot(y=df['UnitPrice'], color='orange')
plt.title("Before Cleaning")
plt.subplot(1, 2, 2)
sns.boxplot(y=df_clean['UnitPrice'], color='skyblue')
plt.title("After Cleaning")
plt.suptitle("Impact of Outlier Removal on Unit Price", fontsize=14)
plt.tight_layout()
plt.show()
plt.figure(figsize=(8,5))
sns.histplot(df_clean['Quantity'] * df_clean['UnitPrice'], bins=50)
plt.title("Revenue Distribution (Cleaned Data)")
plt.xlabel("Revenue")
plt.ylabel("Frequency")
plt.show()
top_countries = df_clean.groupby('Country')['Quantity'].sum().sort_values(ascending=False).head(10)
plt.figure(figsize=(10,5))
top_countries.plot(kind='bar')
plt.title("Top 10 Countries by Sales Volume")
plt.ylabel("Quantity Sold")
plt.xticks(rotation=45)
plt.show()
df_clean['InvoiceDate'] = pd.to_datetime(df_clean['InvoiceDate'])
df_clean['Month'] = df_clean['InvoiceDate'].dt.to_period('M')
monthly_sales = df_clean.groupby('Month')['Quantity'].sum()
plt.figure(figsize=(7,5))
monthly_sales.plot()
plt.title("Monthly Sales Trend")
plt.ylabel("Quantity")
plt.xticks(rotation=45)
plt.show()
Sales volume remained relatively steady between 125,000 and 185,000 units from December through August.
A dramatic surge began in September, reaching a peak near 370,000 units in November, likely driven by Q4 holiday season demand.
Sales dropped sharply back down to around 100,000 units in December, indicating the end of the end-of-year buying cycle.
top_countries = df_clean['Country'].value_counts().head(5).index
df_top = df_clean[df_clean['Country'].isin(top_countries)]
plt.figure(figsize=(10,5))
sns.violinplot(x='Country', y='UnitPrice', data=df_top)
plt.xticks(rotation=45)
plt.title("Unit Price Distribution by Country")
plt.show()
import plotly.express as px
# Aggregate by country
country_sales = df_clean.groupby('Country')['Revenue'].sum().reset_index()
# Plot map
fig = px.choropleth(
country_sales,
locations='Country',
locationmode='country names',
color='Revenue',
color_continuous_scale='Blues',
title='Revenue by Country'
)
fig.show()
Hover over any country on the interactive map to view detailed revenue insights. This map feature uses color intensity to represent revenue metrics, clearly highlighting top-performing regions like the UK at a glance.
And that brings us to the end of the Data Visualization phase. 📊
We started with raw data, explored patterns, identified trends, and turned numbers into stories that can actually be understood.
But visualization answers “What is happening?” The next question is — “What can we predict?” 🔍
🚀 Next up: Machine Learning — where data starts making predictions.
The journey from Messy to Meaningful continues…